package app.util.image.calc
{
	import flash.display.*;
	
	public class BMPUtility
	{
		public static const TOTAL_RANGE:int = 256;
		
       //Calculate threshold using Entropy-algorithm.
		public static function calcThresholdByEntropy(data:BitmapData):Number
		{
			var hist:Array = getGrayHistogram(data);			
			var result:int;
			var classA:Number, classB:Number, v:Number, maxV:Number = 0;
			var calcClass:Function = function (start:int, end:int):Number
			{
				var r:Number = 0, pi:Number;
				var pixelNumber:Number = 0;
				for(var i:int = start;i<end;i++){
					pixelNumber+=hist[i];
				}
	            for (var t:int = start; t < end; t++) 
	            {
	                pi = hist[t] / pixelNumber;
	                if(pi != 0)
	                {
	                	r += pi * (Math.log(pi)/Math.LN2);
	                }
	            }
				return r;
			}
			
			for (var i:int = 1; i < TOTAL_RANGE - 1; i++) {
                classA = calcClass(0, i);
                classB = calcClass(i + 1, TOTAL_RANGE);
                v = - classA - classB;
                if(v > maxV){
                    maxV = v;
                    result = i;
                }
            }
			trace("Threshold calculated by Entropy-algorithm is " + result);
            return result;
		}
		
		//Calculate threshold using Otsu-algorithm.
		public static function calcThreshold(data:BitmapData):Number
		{
			var hist:Array = getGrayHistogram(data);	
			var result:Number = 0;
			var classA_W:Number;
			var classB_W:Number;
			var classA_U:Number;
			var classB_U:Number;
			var v:Number;
			var maxV:Number=0;
		 	var allPixel:Number = data.height * data.width;
			var calcW:Function = function(start:int, end:int):Number{
				var pixelNumber:Number = 0;
				for(var i:int = start; i < end; i++){
					pixelNumber += hist[i];
				}
				return pixelNumber / allPixel;
			}
			var calcU:Function = function(start:int, end:int):Number{
				var pixelNumber:Number = 0;
				var ret:Number = 0;
				for(var i:int = start; i < end; i++){
					pixelNumber += hist[i];
				}
				if(pixelNumber == 0){
					return 0;
				}
				for(var v:int = start; v < end;v++){
					var pi:Number = hist[v] / pixelNumber;
					var val:Number = pi*v;
					ret += val;
				}
				return ret;
			}
			for (var i:int = 1; i < TOTAL_RANGE - 1; i++) {
                classA_W = calcW(0, i);
                classB_W = calcW(i + 1, TOTAL_RANGE);
                classA_U = calcU(0, i);
                classB_U = calcU(i + 1, TOTAL_RANGE);
                v = classA_W * classB_W * Math.pow(classA_U - classB_U, 2);
                if(v > maxV){
                    maxV = v;
                    result = i;
                }
            }
			trace("Threshold calculated by Otsu-algorithm is " + result);
			return result;
		}
		
		//Calculate the average gray level for a RGB color, return a value between 0 - 255.
		public static function calcGrayFromColor(color:uint):uint
		{
			var r:uint = (color & 0x00ff0000) >> 16;
			var g:uint = (color & 0x0000ff00) >> 8;
			var b:uint = color & 0x000000ff;
			return (r + g + b) / 3;
		}
		
		//Calculate the color from gray level.
		public static function calcColorFromGray(gray:uint):uint
		{
			gray = (gray & 0x000000ff);
			var color:uint = (gray << 16 | gray << 8 | gray);
			return color; 
		}
		
		//Get the histogram array of a bitmap data.
		public static function getGrayHistogram(data:BitmapData):Array
		{
			var grayArray:Array = new Array(TOTAL_RANGE); 
			
			for(var c:int = 0; c < TOTAL_RANGE; c++)
			{
				grayArray[c] = 0;
			}

			var w:int = data.width;
			var h:int = data.height;
			for(var i:int = 0; i < w; i++)
			{
				for(var j:int = 0; j < h; j++)
				{
					var color:uint = data.getPixel(i, j);
					var gray:uint = calcGrayFromColor(color);
					grayArray[gray] += 1;
				}
			}
			
			return grayArray;
		}
		
	}
}